package com.biz.primus.ms.behavior.service;


import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.biz.primus.common.utils.JsonUtils;
import com.biz.primus.ms.base.service.AbstractBaseService;
import org.springframework.stereotype.Service;
import sun.misc.BASE64Encoder;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.net.ssl.HttpsURLConnection;
import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.MessageDigest;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.zip.GZIPOutputStream;
/**
 * @author jiangnan
 * @Description:
 */
@Service
public class UploadLogService extends AbstractBaseService {
    // 用户需要修改
    private static String biz_code = "${biz_code}";
    private static String token = "${token}";
    private static String url_prefix = "${host}/${dplus_org_code}/re/uploadlog?businessName=recommend";
    // 用户需要修改, 用户的access_key_id与access_key_secret
    private static final String access_key_id = "";
    private static final String access_key_secret = "";

    /**
     *  需要测试的话，请写到java/main/test里，以测试类进行。请不要用main方式进行测试.
     * @param args
     * @throws IOException
     */
    @Deprecated
    public static void deprecated_main(String[] args) throws IOException {
        // 用户的logs
        // 每条日志是字符串
        List<String> logs = new LinkedList<String>();
        // 或者每条日志是JSONObect
        // List<JSONObject> logs = new LinkedList<JSONObject>();
        // 用户添加日志，要么积累一分钟的量, 要么积累到2000~3000条
        logs.add("{\"user_id\":\"0\", \"item_id\":0, \"action\":\"view\",\"bhv_datetime\": \"2016-05-26 00:00:00\"}");
        if (logs == null || logs.size() == 0) {
            return;
        }
        // 积累的多条日志JSONArray数组
        JSONArray content = new JSONArray();
        // 用户向content中填写多条日志(要么积累一分钟的量, 要么积累到2000~3000条)
        content.addAll(logs);
        // 上传日志
//        sendPostHTTPS(content);

    }
    /**
     * 使用HTTPS对多条日志进行GZIP压缩并上传
     *
     * @param content 日志JSONArray数组(要么积累一分钟的量, 要么积累到2000~3000条)
     * @throws IOException
     */
    public void sendPostHTTPS(JSONArray content) throws IOException {
        if (content != null && content.size() > 0) {
            HttpsURLConnection conn = null;
            try {
                BufferedReader in = null;
                URL upload_url = new URL(url_prefix + "&customerName=" + biz_code + "&token=" + token);
                StringBuilder result = new StringBuilder();
                /*
                 * http header 参数 必须设置
                 */
                String method = "POST";
                String accept = "application/json";
                String content_type = "application/json";
                String path = upload_url.getFile();
                String date = toGMTString(new Date());
                String content_encoding = "gzip";
                // 1. 对日志JSONArray进行FGZIP压缩
                byte[] body = compressToByte(content);
                // 2. 对body做MD5+BASE64加密
                String body_md5 = MD5Base64(body);
                String string_to_sign = method + "\n" + accept + "\n" + body_md5 + "\n" + content_type + "\n" + date + "\n" + path;
                // 3.计算 HMAC-SHA1
                String signature = HMACSha1(string_to_sign, access_key_secret);
                // 4.得到 authorization header
                String auth_header = "Dataplus " + access_key_id + ":" + signature;

                // 发起连接
                conn = (HttpsURLConnection) upload_url.openConnection();
                // 设置超时, 建议1分钟, 可以更大一点
                conn.setConnectTimeout(60000);
                conn.setReadTimeout(60000);
                // 设置请求方法
                conn.setRequestMethod("POST");
                // 设置通用的请求属性
                conn.setRequestProperty("accept", accept);
                conn.setRequestProperty("content-type", content_type);
                conn.setRequestProperty("date", date);
                conn.setRequestProperty("Authorization", auth_header);
                // 必须要设置为GZIP，否则服务器会不接受
                conn.setRequestProperty("Content-Encoding", content_encoding);
                // 发送POST请求必须设置如下两行
                conn.setDoOutput(true);
                conn.setDoInput(true);
                // 设置是非缓存
                conn.setUseCaches(false);
                // 传输body流 必须GZIP加密后字节数组
                DataOutputStream data_stream = new DataOutputStream(conn.getOutputStream());
                data_stream.write(body);
                data_stream.flush();
                data_stream.close();
                if (conn.getResponseCode() != 200) {
                    System.err.println("日志API连接失败!");
                } else {
                    // 返回jsonobject
                    in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
                    String line = null;
                    while ((line = in.readLine()) != null) {
                        result.append(line);
                    }
                    if (result.length() > 0) {

                        JSONObject rsp_json = JsonUtils.json2Obj(result.toString(),JSONObject.class);
                        int success = rsp_json.getIntValue("success");
                        if (success == 1) {
                            System.out.println("数据上传成功");
                        } else {
                            System.out.println("数据上传失败, 出错信息: " + rsp_json.getString("errMsg"));
                        }
                    }
                }
            } catch (MalformedURLException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (conn != null) {
                    conn.disconnect();
                }
            }
        }
    }
    /*
     * 计算MD5+BASE64
     */
    public static String MD5Base64(byte[] utfBytes) {
        if (utfBytes == null) {
            return null;
        }
        String encodeStr = "";
        MessageDigest mdTemp;
        try {
            mdTemp = MessageDigest.getInstance("MD5");
            mdTemp.update(utfBytes);
            byte[] md5Bytes = mdTemp.digest();
            BASE64Encoder b64Encoder = new BASE64Encoder();
            encodeStr = b64Encoder.encode(md5Bytes);
        } catch (Exception e) {
            throw new Error("Failed to generate MD5 : " + e.getMessage());
        }
        return encodeStr;
    }
    /*
     * 计算 HMAC-SHA1
     */
    public static String HMACSha1(String data, String key) {
        String result;
        try {
            SecretKeySpec signingKey = new SecretKeySpec(key.getBytes(), "HmacSHA1");
            Mac mac = Mac.getInstance("HmacSHA1");
            mac.init(signingKey);
            byte[] rawHmac = mac.doFinal(data.getBytes());
            result = (new BASE64Encoder()).encode(rawHmac);
        } catch (Exception e) {
            throw new Error("Failed to generate HMAC : " + e.getMessage());
        }
        return result;
    }
    /*
     * 等同于javaScript中的 new Date().toUTCString();
     */
    public static String toGMTString(Date date) {
        SimpleDateFormat df = new SimpleDateFormat("E, dd MMM yyyy HH:mm:ss z", Locale.UK);
        df.setTimeZone(new java.util.SimpleTimeZone(0, "GMT"));
        return df.format(date);
    }
    /*
     * 字符串压缩为字节数组
     */
    public static byte[] compressToByte(JSONArray content) {
        try {
            int len1 = content.toString().getBytes("utf-8").length;
            System.out.println("压缩前数据大小(B): " + len1);
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            GZIPOutputStream gzip = new GZIPOutputStream(out);
            gzip.write(content.toString().getBytes("utf-8"));
            gzip.finish();
            gzip.flush();
            gzip.close();
            byte[] data = out.toByteArray();
            int len2 = data.length;
            System.out.println("压缩后数据大小(B): " + len2);
            System.out.println("压缩率: " + len2 * 1.0 / len1);
            return data;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }



}
